---
title: "Error Handling"
group: "Errors"
groupOrder: 0
---

In ZSA, errors are handled using the `ZSAError` class. This class extends the built-in `Error` class and adds some additional properties to make it easier to handle errors in your server actions.

## Throwing Errors

### Just throw a string!

If all you care about is the error message, you can just throw a string:

```ts
throw "You are not authorized to perform this action"
```

This string will be accessible in the `data` property of the error object.

```ts 
const [data, error] = await myAction()

if (error) {
  console.log(error.data) // "You are not authorized to perform this action"
  console.log(error.message) // "You are not authorized to perform this action"
  console.log(error.code) // "ERROR"
}
```

### Throw a ZSAError

To throw an error in a server action with a code, you can simply throw a new instance of the `ZSAError` class:

```ts 
throw new ZSAError(
  "NOT_AUTHORIZED",
  "You are not authorized to perform this action"
)
```

The first argument to the `ZSAError` constructor is the error code, and the second argument is the error data. The error data can be any value, but it is typically a string or an object.

If the error data is an instance of the built-in `Error` class, the `ZSAError` instance will inherit the `message`, `name`, and `cause` properties from the error data.

If the error data is a string and the `message` property is not set, the `message` property will be set to the error data.

## Error Codes

ZSA defines a set of error codes that can be used to identify the type of error that occurred. These error codes are defined in the `ERROR_CODES` enum:

```ts 
const ERROR_CODES = {
  INPUT_PARSE_ERROR: "INPUT_PARSE_ERROR",
  OUTPUT_PARSE_ERROR: "OUTPUT_PARSE_ERROR",
  ERROR: "ERROR",
  NOT_AUTHORIZED: "NOT_AUTHORIZED",
  TIMEOUT: "TIMEOUT",
  INTERNAL_SERVER_ERROR: "INTERNAL_SERVER_ERROR",
  FORBIDDEN: "FORBIDDEN",
  NOT_FOUND: "NOT_FOUND",
  CONFLICT: "CONFLICT",
  PRECONDITION_FAILED: "PRECONDITION_FAILED",
  PAYLOAD_TOO_LARGE: "PAYLOAD_TOO_LARGE",
  METHOD_NOT_SUPPORTED: "METHOD_NOT_SUPPORTED",
  UNPROCESSABLE_CONTENT: "UNPROCESSABLE_CONTENT",
  TOO_MANY_REQUESTS: "TOO_MANY_REQUESTS",
  CLIENT_CLOSED_REQUEST: "CLIENT_CLOSED_REQUEST",
  INSUFFICIENT_CREDITS: "INSUFFICIENT_CREDITS",
  PAYMENT_REQUIRED: "PAYMENT_REQUIRED",
} as const
```

## Typed Errors

ZSA also provides a typed version of the `ZSAError` class called `TZSAError`. This class is a generic type that takes a Zod schema as a type parameter. The schema represents the input schema of the server action.

If the error code is `INPUT_PARSE_ERROR`, the `TZSAError` instance will have additional properties that provide information about the validation errors that occurred:

- `fieldErrors`: An object that maps field names to arrays of error messages.
- `formErrors`: An array of error messages that apply to the entire form.
- `formattedErrors`: A formatted version of the validation errors.

These properties are inferred from the Zod schema that is passed to the `TZSAError` type.

## Handling Errors

When calling a server action, you can handle errors by destructuring the result and checking for an error:

```ts 
const [data, error] = await myAction()

if (error) {
  if (error.code === "NOT_AUTHORIZED") {
    // Handle not authorized error
  } else if (error.code === "INPUT_PARSE_ERROR") {
    // Handle input validation errors
    console.log(error.fieldErrors)
    console.log(error.formErrors)
    console.log(error.formattedErrors)
  } else {
    // Handle other errors
  }
}
```

By using the error code and the additional error properties, you can handle different types of errors in different ways.

## OpenAPI Error Status Codes

`zsa-openapi` maps ZSA error codes to HTTP status codes. This can be useful when returning errors from an API endpoint:

```ts 
switch (error.code) {
  case "INTERNAL_SERVER_ERROR":
    return 500
  case "INPUT_PARSE_ERROR":
    return 400
  case "OUTPUT_PARSE_ERROR":
    return 500
  case "ERROR":
    return 500
  case "NOT_AUTHORIZED":
    return 401
  case "TIMEOUT":
    return 408
  case "FORBIDDEN":
    return 403
  case "NOT_FOUND":
    return 404
  case "CONFLICT":
    return 409
  case "PRECONDITION_FAILED":
    return 412
  case "PAYLOAD_TOO_LARGE":
    return 413
  case "METHOD_NOT_SUPPORTED":
    return 405
  case "UNPROCESSABLE_CONTENT":
    return 422
  case "TOO_MANY_REQUESTS":
    return 429
  case "CLIENT_CLOSED_REQUEST":
    return 499
  case "INSUFFICIENT_CREDITS":
  case "PAYMENT_REQUIRED":
    return 402
  default:
    return 500
}
```
